/**
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.solr.search;

/**
 * <code>DocSlice</code> implements DocList as an array of docids and optional
 * scores.
 * 
 * @version $Id: DocSlice.java 790938 2009-07-03 15:09:21Z yonik $
 * @since solr 0.9
 */
public class DocSlice extends DocSetBase implements DocList {
	final int offset; // starting position of the docs (zero based)
	final int len; // number of positions used in arrays
	final int[] docs; // a slice of documents (docs 0-100 of the query)

	final float[] scores; // optional score list
	final int matches;
	final float maxScore;

	/**
	 * Primary constructor for a DocSlice instance.
	 * 
	 * @param offset
	 *            starting offset for this range of docs
	 * @param len
	 *            length of results
	 * @param docs
	 *            array of docids starting at position 0
	 * @param scores
	 *            array of scores that corresponds to docs, may be null
	 * @param matches
	 *            total number of matches for the query
	 */
	public DocSlice(int offset, int len, int[] docs, float[] scores,
			int matches, float maxScore) {
		this.offset = offset;
		this.len = len;
		this.docs = docs;
		this.scores = scores;
		this.matches = matches;
		this.maxScore = maxScore;
	}

	public DocList subset(int offset, int len) {
		if (this.offset == offset && this.len == len)
			return this;

		// if we didn't store enough (and there was more to store)
		// then we can't take a subset.
		int requestedEnd = offset + len;
		if (requestedEnd > docs.length && this.matches > docs.length)
			return null;
		int realEndDoc = Math.min(requestedEnd, docs.length);
		int realLen = Math.max(realEndDoc - offset, 0);
		if (this.offset == offset && this.len == realLen)
			return this;
		return new DocSlice(offset, realLen, docs, scores, matches, maxScore);
	}

	public boolean hasScores() {
		return scores != null;
	}

	public float maxScore() {
		return maxScore;
	}

	public int offset() {
		return offset;
	}

	public int size() {
		return len;
	}

	public int matches() {
		return matches;
	}

	public long memSize() {
		return (docs.length << 2) + (scores == null ? 0 : (scores.length << 2))
				+ 24;
	}

	public boolean exists(int doc) {
		int end = offset + len;
		for (int i = offset; i < end; i++) {
			if (docs[i] == doc)
				return true;
		}
		return false;
	}

	// Hmmm, maybe I could have reused the scorer interface here...
	// except that it carries Similarity baggage...
	public DocIterator iterator() {
		return new DocIterator() {
			int pos = offset;
			final int end = offset + len;

			public boolean hasNext() {
				return pos < end;
			}

			public Integer next() {
				return nextDoc();
			}

			/**
			 * The remove operation is not supported by this Iterator.
			 */
			public void remove() {
				throw new UnsupportedOperationException(
						"The remove  operation is not supported by this Iterator.");
			}

			public int nextDoc() {
				return docs[pos++];
			}

			public float score() {
				return scores[pos - 1];
			}
		};
	}

	@Override
	public DocSet intersection(DocSet other) {
		if (other instanceof SortedIntDocSet || other instanceof HashDocSet) {
			return other.intersection(this);
		}
		HashDocSet h = new HashDocSet(docs, offset, len);
		return h.intersection(other);
	}

	@Override
	public int intersectionSize(DocSet other) {
		if (other instanceof SortedIntDocSet || other instanceof HashDocSet) {
			return other.intersectionSize(this);
		}
		HashDocSet h = new HashDocSet(docs, offset, len);
		return h.intersectionSize(other);
	}
}
